/*
 * This file is part of the Boring Engine's Kirby Game.
 *
 * Copyright (c) 2011 by pf5234 <pf5234@users.sourceforge.net>.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

package kirby;

import com.googlecode.boringengine.Graphics;
import com.googlecode.boringengine.Log;
import com.googlecode.boringengine.Sprite;
import com.googlecode.boringengine.lua.LuaInterpreter;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Scanner;

public class Level {

    private HashMap<Integer, Tile> tilemap;
    private HashMap<Integer, int[][]> maps;
    private boolean[][] solidMap;
    private int music, kirbyX, kirbyY, width, height;
    private String name;
    private Integer[] layerSorted;
    private ArrayList<GameObject> objs;

    public Level(String fileName) {
        boolean loadObjects = false;
        Log.debug("Loading %s...", fileName);
        tilemap = new HashMap<Integer, Tile>();
        maps = new HashMap<Integer, int[][]>();
        objs = new ArrayList<GameObject>();
        Scanner in = new Scanner(Resources.getResource("levels/" + fileName + ".lvl"));
        int[][] map = null; int row = 0;
        while (in.hasNextLine()) {
            String line = in.nextLine();
            String[] tokens = line.split(" ", 2);
            if (tokens[0].equals("name"))
                name = tokens[1];
            else if (tokens[0].equals("music"))
                music = Integer.parseInt(tokens[1]);
            else if (tokens[0].equals("tilemap"))
                loadTileMap(tokens[1]);
            else if (tokens[0].equals("map")) {
                tokens = line.split(" ");
                int w = Integer.parseInt(tokens[1]), h = Integer.parseInt(tokens[2]);
                if (w > width) width = w;
                if (h > height) height = h;
                map = new int[h][w];
                maps.put(Integer.parseInt(tokens[3]), map);
                row = 0;
            } else if (tokens[0].equals("kirby")) {
                tokens = line.split(" ");
                kirbyX = Integer.parseInt(tokens[1]);
                kirbyY = Integer.parseInt(tokens[2]);
            } else if (tokens[0].equals("objects")) {
                loadObjects = true;
                break;
            } else {
                tokens = line.split(" ");
                for (int i = 0; i < tokens.length; i++) {
                    int tok = Integer.parseInt(tokens[i]);
                    map[row][i] = tok;
                }
                row++;
            }
        }
        map = maps.get(0);
        solidMap = new boolean[map.length][map[0].length];
        for (int i = 0; i < map.length; i++)
            for (int j = 0; j < map[i].length; j++)
                if (map[i][j] != 0)
                    solidMap[i][j] = tilemap.get(map[i][j]).solid;
        layerSorted = maps.keySet().toArray(new Integer[0]);
        Arrays.sort(layerSorted);
        width *= 16; height *= 16;
        if (!loadObjects) return;
        while (in.hasNextLine()) {
            String line = in.nextLine();
            String[] tokens = line.split(" ", 6);
            String className = "kirby." + tokens[0] + "." + tokens[1];
            String[] classTokens = tokens.length == 6 ? tokens[5].split(" ") : new String[0];
            try {
                Class<?> cl = Level.class.getClassLoader().loadClass(className);
                Constructor<?> c = cl.getConstructor(int.class, int.class, String[].class);
                Object o = c.newInstance(Integer.parseInt(tokens[2]), Integer.parseInt(tokens[3]),
                        classTokens);
                objs.add((GameObject) o);
                if (tokens.length >= 5 && !tokens[4].equals("_"))
                    LuaInterpreter.addObject(o, tokens[4]);
            } catch (Exception ex) {
                Log.warnException(ex, "Error loading %s %s (class %s)",
                        tokens[0], tokens[1], className);
            }
        }
    }
    
    public void drawBackground() {
        for (int layer : layerSorted) {
            if (layer > 0) break;
            drawLayer(layer);
        }
    }

    public void drawForeground() {
        for (int layer : layerSorted) {
            if (layer <= 0) continue;
            drawLayer(layer);
        }
    }
    
    public boolean[][] getSolidMap() {
        return solidMap;
    }

    public int getHeight() {
        return height;
    }
    
    public int getMusic() {
        return music;
    }
    
    public String getName() {
        return name;
    }

    public ArrayList<GameObject> getObjects() {
        return objs;
    }

    public int getSpawnX() {
        return kirbyX;
    }

    public int getSpawnY() {
        return kirbyY;
    }

    public int getWidth() {
        return width;
    }

    private void loadTileMap(String fileName) {
        Scanner in = new Scanner(Resources.getResource("levels/" + fileName + ".tile"));
        while (in.hasNextLine()) {
            String line = in.nextLine();
            String[] token = line.split(" ", 3);
            Tile t = new Tile();
            t.solid = Boolean.parseBoolean(token[1]);
            t.s = Graphics.loadSpriteFromFile("resources/gfx/tiles/" + token[2] + ".sprite");
            tilemap.put(Integer.parseInt(token[0]), t);
        }
    }

    private void drawLayer(int layer) {
        int[][] map = maps.get(layer);
        for (int y = 0; y < map.length; y++) {
            for (int x = 0; x < map[y].length; x++) {
                if (map[y][x] == 0) continue;
                if (!tilemap.containsKey(map[y][x])) {
                    Log.warn("Map at %d %d (tile %d) doesn't exist", x, y, map[y][x]);
                    map[y][x] = 0;
                    continue;
                }
                int sprx = x * 16;
                int spry = y * 16;
                Graphics.drawSprite(tilemap.get(map[y][x]).s, sprx, spry);
            }
        }
    }
    
    private class Tile {
        Sprite s;
        boolean solid;
    }
}
